﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting.Channels;
using System.Runtime.Serialization.Formatters;
using System.Collections;
using System.Runtime.Remoting.Channels.Tcp;
using System.Threading;
using IM.gerenciadores;
using System.Diagnostics;
using System.Runtime.Remoting.Proxies;
using System.IO;
using InterfaceRemota;
using Gerenciamento;

namespace IM
{
    [Serializable()]
    public partial class FormPrincipal : Form, IUsuario
    {

        #region delegadores de funções

        delegate void SetTextCallback(
            List<IMensagem> mensagens,
            List<IUsuario> usuariosConectados);

        delegate void PedeAtencao();

        #endregion delegadores de funções


        #region métodos acessores

        private String ip;
        private short porta;

        private FormLogin formLogin;
        public FormLogin FormLogin
        {
            get { return formLogin; }
            set { formLogin = value; }
        }

        private String nome;
        public String Nome
        {
            get { return nome; }
            set { nome = value; }
        }

        private String login;
        public String Login
        {
            get { return login; }
            set { login = value; }
        }

        private List<IMensagem> mensagens;
        public List<IMensagem> Mensagens
        {
            get
            {
                if (mensagens == null)
                {
                    mensagens = new List<IMensagem>();
                }
                return mensagens;
            }
            set
            {
                mensagens = value;
                //iniciaThread();
            }
        }

        private List<IUsuario> usuariosConectados;
        public List<IUsuario> UsuariosConectados
        {
            get
            {
                if (usuariosConectados == null)
                {
                    usuariosConectados = new List<IUsuario>();
                }
                return usuariosConectados;
            }
            set
            {
                usuariosConectados = value;
                //iniciaThread();
            }
        }

        private IServidor servidor;
        public IServidor Servidor
        {
            get
            {
                if (servidor == null)
                {
                    //"10.11.20.76"
                    //"localhost"
                    servidor = gerenciadores.GerenciadorDeConexaoCliente.
                        configuraServidor(this.ip, "9000", this.porta, "Gerenciamento");
                }
                return servidor;
            }
        }

        #endregion métodos acessores


        #region inicialização e configurações

        public FormPrincipal(
            IUsuario usuario, FormLogin formLogin, String ip, short porta)
        {
            this.ip = ip;
            this.porta = porta;
            InitializeComponent();
            listUsuarios.LargeImageList = new ImageList();
            listUsuarios.LargeImageList.ImageSize = new Size(48, 48);
            Control.CheckForIllegalCrossThreadCalls = false;
            this.formLogin = formLogin;
            this.login = usuario.Login;
            this.nome = GerenciadorDeUsuarios.getInstancia().getUserNamePorLogin(usuario.Login);
            txtUsuarioLogado.Text = this.nome;
            this.Text += " - " + this.nome;
            //threadMensagemPrivada();
        }

        private void FormPrincipal_Load(object sender, EventArgs e)
        {
            Servidor.conectaUsuario(this);
            this.TopLevel = true;
            //btnCorLetra.BackColor = txtMensagem.ForeColor;
            btnFonte.ForeColor = txtMensagem.ForeColor;
            //btnCorFundo.BackColor = txtMensagem.BackColor;
            btnFonte.BackColor = txtMensagem.BackColor;
        }

        private void FormPrincipal_FormClosed(object sender, FormClosedEventArgs e)
        {
            Servidor.desconectaUsuario(this);
            FormLogin.Close();
        }

        #endregion inicialização e configurações


        #region envia mensagem

        private void btnEnviar_Click(object sender, EventArgs e)
        {
            GerenciadorDeTexto gText = GerenciadorDeTexto.getInstancia();
            GerenciadorDeUsuarios gUser = GerenciadorDeUsuarios.getInstancia();
            String strMensagem = txtMensagem.Text;
            if ((strMensagem != null) && (strMensagem.Trim().Length > 0))
            {
                IUsuario destino = null;
                
                IMensagem mensagem = new Gerenciamento.MensagemImpl();
                gText.configuraCorMensagem(mensagem, txtMensagem.ForeColor);
                gText.configuraFonteMensagem(mensagem, txtMensagem.Font);
                
                String mensagemPronta = Nome + " diz";

                destino = gUser.recuperaUsuarioSelecionadoDoListView(listUsuarios);
                if (destino != null && destino != this)
                {
                    mensagemPronta += " à " + destino.Nome;
                }
                mensagemPronta += " (" + DateTime.Now.ToShortTimeString() + "): ";
                
                Boolean isPrivado =
                    (cbPrivado.Enabled) && (cbPrivado.Checked)
                    && destino != null && destino != this;
                if (isPrivado)
                {
                    mensagemPronta += " (PRIVADO): ";
                }
                
                String msgmEnviada = GerenciadorDeCodificacao.codificaString(mensagemPronta + strMensagem);
                mensagem.Mensagem = msgmEnviada;
                Servidor.enviaMensagem(mensagem, this, destino, isPrivado);

                listUsuarios.SelectedIndices.Clear();
                cbPrivado.Checked = false;
                txtMensagem.Text = "";
            }
            else
            {
                MessageBox.Show("Pra que um comunicador se você não digita mensagem???");
            }
        }

        #endregion envia mensagem


        #region recebe mensagem e atualiza conversa e usuários

        IMensagem msg;
        public void recebeMensagem(IMensagem mensagem)
        {
            Mensagens.Add(mensagem);
            msg = mensagem;
            Thread thread = new Thread(threadConversa);
            thread.Start();
        }

        private void threadConversa()
        {
            Thread.Sleep(10);
            if (txtConversa.InvokeRequired)
            {
                SetTextCallback cb = new SetTextCallback(setMensagensEUsuarios);
                txtConversa.Invoke(cb, Mensagens, UsuariosConectados);
            }
            else
            {
                atualizaForm();
            }
        }

        private void setMensagensEUsuarios(
            List<IMensagem> mensagens,
            List<IUsuario> usuariosConectados)
        {
            preencheConversa(mensagens);
            preencheUsuarios(usuariosConectados);
        }

        public void atualizaForm()
        {
            preencheConversa(Mensagens);
            preencheUsuarios(UsuariosConectados);
        }

        private void preencheConversa(
            List<IMensagem> mensagens)
        {
            GerenciadorDeTexto gText = GerenciadorDeTexto.getInstancia();
            if (msg != null)
            {
                String msgmDecodificada = GerenciadorDeCodificacao.decodificaString(msg.Mensagem);
                Color cor = Color.FromArgb(msg.A, msg.R, msg.G, msg.B);
                txtConversa.SelectionColor = cor;
                FontStyle estilo = gText.configuraEstilo(msg.EstiloFonte);
                txtConversa.SelectionFont = new Font(msg.NomeFonte, msg.TamanhoFonte, estilo);
                if (txtConversa.Text.Length == 0)
                {
                    txtConversa.SelectedText += msgmDecodificada;
                }
                else
                {
                    txtConversa.SelectedText += Environment.NewLine + msgmDecodificada;
                }
            }
            txtConversa.Select(txtConversa.Text.Length, 0);
            txtConversa.ScrollToCaret();
        }

        private void preencheUsuarios(
            List<IUsuario> usuariosConectados)
        {
            listUsuarios.Items.Clear();
            foreach (IUsuario user in usuariosConectados)
            {
                String login = user.Login;
                String userName = user.Nome;
                byte[] imagemAConverter = Servidor.getAvatarPorLogin(login);
                Image avatar = GerenciadorDeUsuarios.getInstancia().getByteArrayAsImage(imagemAConverter);
                listUsuarios.LargeImageList.Images.Add(login, avatar);

                ListViewItem item = new ListViewItem();
                item.Text = userName;
                item.ImageKey = login;
                item.Tag = user;
                listUsuarios.Items.Add(item);
            }
        }

        #endregion recebe mensagem e atualiza conversa e usuários


        #region manipula componentes

        private void listUsuarios_SelectedIndexChanged(object sender, EventArgs e)
        {
            cbPrivado.Enabled = listUsuarios.SelectedItems.Count > 0;
        }

        private void listUsuarios_ItemSelectionChanged(object sender, ListViewItemSelectionChangedEventArgs e)
        {
            foreach (ListViewItem item in listUsuarios.Items)
            {
                item.BackColor = Color.White;
                item.ForeColor = Color.Black;
                item.Font = new Font(item.Font.FontFamily, 9, FontStyle.Regular);
            }
            e.Item.BackColor = Color.DarkBlue;
            e.Item.ForeColor = Color.White;
            e.Item.Font = new Font(e.Item.Font.FontFamily, 11, FontStyle.Bold);
        }

        #endregion manipula componentes


        #region formata texto

        private void btnCorLetra_Click(object sender, EventArgs e)
        {
            this.cores.ShowDialog();
            txtMensagem.ForeColor = cores.Color;
            //btnCorLetra.BackColor = cores.Color;
            btnFonte.ForeColor = cores.Color;
        }

        private void btnCorFundo_Click(object sender, EventArgs e)
        {
            this.cores.ShowDialog();
            txtMensagem.BackColor = cores.Color;
            txtConversa.BackColor = cores.Color;
            //btnCorFundo.BackColor = cores.Color;
            btnFonte.BackColor = cores.Color;
        }

        private void btnFonte_Click(object sender, EventArgs e)
        {
            fontes.ShowDialog();
            txtMensagem.Font = fontes.Font;
        }

        #endregion formata texto


        #region gerencia pedidos de atenção

        private void btnChamaAtencao_Click(object sender, EventArgs e)
        {
            Thread threadAtencao = new Thread(threadValidaAtencao);
            threadAtencao.Start();
            IUsuario destino = null;
            if (listUsuarios.SelectedItems.Count > 0)
            {
                ListViewItem item = listUsuarios.SelectedItems[0];
                destino = (IUsuario)item.Tag;
                if (destino == this)
                {
                    destino = null;
                }
            }
            Servidor.chamaAtencao(this, destino);
            listUsuarios.SelectedIndices.Clear();
            cbPrivado.Checked = false;
        }

        private void threadValidaAtencao()
        {
            btnChamaAtencao.Enabled = false;
            Thread.Sleep(30000);
            btnChamaAtencao.Enabled = true;
        }

        public void chamaAtencao()
        {
            iniciaThreadAtencao();
        }

        public void iniciaThreadAtencao()
        {
            Thread thread = new Thread(new ThreadStart(threadAtencao));
            thread.Start();
        }

        private void threadAtencao()
        {
            Thread.Sleep(10);

            if (this.InvokeRequired)
            {
                PedeAtencao atencao = new PedeAtencao(moveTela);
                this.Invoke(atencao);
            }
            else
            {
                moveTela();
            }
        }

        private void moveTela()
        {
            if (this.WindowState.Equals(FormWindowState.Maximized))
            {
                GerenciadorDeAtencao.moveTelaMaximizada(this);
            }
            else
            {
                GerenciadorDeAtencao.moveTela(this);
            }
        }


        #endregion gerencia pedidos de atenção



        #region envio de arquivo

        private byte[] arquivo;
        private String fileName;
        private IUsuario destino;
        private void btnEnviarArquivo_Click(object sender, EventArgs e)
        {
            GerenciadorDeUsuarios gUser = GerenciadorDeUsuarios.getInstancia();
            GerenciadorDeArquivo gFile = GerenciadorDeArquivo.getInstancia();
            destino = gUser.recuperaUsuarioSelecionadoDoListView(listUsuarios);
            enviaArquivo.ShowDialog();
            String filePath = enviaArquivo.FileName;
            fileName = enviaArquivo.SafeFileName;
            if (fileName.Length > 0)
            {
                if (File.Exists(filePath) && (destino != null))
                {
                    arquivo = gFile.getArquivoAsByteArray(filePath);
                    new Thread(enviaOArquivo).Start();
                }
                else
                {
                    MessageBox.Show("Arquivo não existe ou nenhum usuário foi selecionado.");
                }
            }
        }

        
        private void enviaOArquivo()
        {
            byte[] arquivoCodificado = GerenciadorDeCodificacao.codificaArquivo(arquivo);
            Servidor.enviaArquivo(arquivoCodificado, fileName, destino, this);
        }

        public void recebeArquivo(byte[] arquivo, String fileName,
            IUsuario origem, IMensagem mensagem)
        {
            byte[] arquivoDecodificado = GerenciadorDeCodificacao.decodificaArquivo(arquivo);
            GerenciadorDeArquivo gFile = GerenciadorDeArquivo.getInstancia();
            
            DialogResult dr = 
                MessageBox.Show(origem.Nome + " está lhe enviando um arquivo. Deseja salvá-lo?",
                "BoRsO iM - File Transfer", MessageBoxButtons.YesNo);
            if (dr.Equals(DialogResult.Yes))
            {
                recebeMensagem(mensagem);
                salvaArquivo.FileName = fileName;
                salvaArquivo.ShowDialog();
                String filePath = salvaArquivo.FileName;
                if (filePath.Length > 0)
                {
                    gFile.salvaArquivo(filePath, arquivoDecodificado);
                }
            }


        }


        #endregion


        #region emoticons

        private void btnEmoticon_Click(object sender, EventArgs e)
        {/*
            Image avatar = GerenciadorDeAvatar.getInstancia().getAvatarPorLogin(this.Login);
            IDataObject clipboardAntigo = Clipboard.GetDataObject();
            Clipboard.SetDataObject(avatar);
            DataFormats.Format df = DataFormats.GetFormat(DataFormats.Bitmap);
            if (txtMensagem.CanPaste(df))
            {
                txtMensagem.Paste(df);
            }
            Clipboard.SetDataObject(clipboardAntigo);*/
        }

        #endregion


        #region MENSAGENS PRIVADAS (próxima Versão)


        private Hashtable usuariosNoPrivado;
        public Hashtable UsuariosNoPrivado
        {
            get {
                if (usuariosNoPrivado == null)
                {
                    usuariosNoPrivado = new Hashtable();
                }
                return usuariosNoPrivado;
            }
            set { usuariosNoPrivado = value; }
        }

        private void listUsuarios_DoubleClick(object sender, EventArgs e)
        {/*
            IUsuario destino =
                GerenciadorDeUsuarios.getInstancia().recuperaUsuarioSelecionadoDoListView(listUsuarios);
            if (destino != null )//&& destino != this)
            {
                //getFormPrivado(destino).showForm();
                GerenciadorDeXML.getInstancia().geraXMLForm("informacoesForm", this.Login, destino, Mensagens[0]);
            }*/
        }

        private IPrivado getFormPrivado(IUsuario user)
        {
            IPrivado fPrivado = null;
            if (UsuariosNoPrivado.ContainsKey(user))
            {
                fPrivado = (IPrivado)UsuariosNoPrivado[user];
            }
            else
            {
                fPrivado = criaFormPrivado(user);
            }
            return fPrivado;
        }

        private IPrivado criaFormPrivado(IUsuario user)
        {
            IPrivado formPrivado = new FormPrivado(this, user, Servidor);
            formPrivado.titulo(user.Nome + " conversa com você!");
            UsuariosNoPrivado.Add(user, formPrivado);
            return formPrivado;
        }

        public void enviaMensagemPrivada(IUsuario destino,
            IMensagem mensagem)
        {
            Servidor.enviaMensagemPrivada(this, destino, mensagem);
        }

        public void recebeMensagemPrivada(IUsuario origem,
            IMensagem mensagem)
        {
            GerenciadorDeXML.getInstancia().geraXMLForm("informacoesForm", this.Login, origem, mensagem);
        }

        private void threadMensagemPrivada()
        {
            Thread th = new Thread(monitoraPastaTemp);
            th.Start();
        }


        private void monitoraPastaTemp()
        {
            Form f = new Form();
            String path = Environment.GetEnvironmentVariable("Temp") + "\\" + this.Login + "\\informacoesForm.xml";
            bool achouArquivo = true;
            do
            {
                achouArquivo = File.Exists(path);
            } while (!achouArquivo);
            //FormTeste f = new FormTeste();
            //f.Show();
            //Thread.Sleep(5000);
        }

        #endregion

    }
}
